package net

import (
	"net"
	"rpc-demo/handler"
	"rpc-demo/net/codec"
	"rpc-demo/net/serialize"
	"rpc-demo/net/util"
)

const TYPE = "tcp"

type NexusChanel[T any] struct {
	conn net.Conn
	sr   serialize.Serializer[T]
}

func (chanel *NexusChanel[T]) Write(data T) error {
	if bs, err2 := chanel.sr.Serialize(data); err2 != nil {
		return err2
	} else {
		_, err := chanel.conn.Write(codec.Encode(bs))
		return err
	}
}

func (chanel *NexusChanel[T]) Close() error {
	return chanel.conn.Close()
}

func closeConn(conn net.Conn) {
	if conn != nil {
		err := conn.Close()
		handler.ErrorHandler(err)
	}
}

func getSr[T any](name string) serialize.Serializer[T] {
	switch name {
	case "gob":
		return serialize.GobSerializer[T]{}
	default:
		return serialize.GobSerializer[T]{}
	}
}

func (chanel *NexusChanel[T]) handleRead(rdh readHandler[T]) {
	conn := chanel.conn
	defer closeConn(conn)
	data := make([]byte, 10)
	splitLength := 4
	var length = 0
	var byteData []byte
	for {
		n, err := conn.Read(data)
		if handler.ErrorHandler(err) {
			continue
		}
		if n <= 0 {
			continue
		}
		if len(data) < splitLength {
			continue
		}
		if length == 0 {
			bytes := data[0:splitLength]
			length = util.BytesToInt(bytes)
			byteData = make([]byte, 0, length)
			d := n - splitLength
			var availableBytes []byte
			if d < length {
				availableBytes = data[splitLength:n]
				length -= d
				byteData = append(byteData, availableBytes...)
			} else {
				availableBytes = data[splitLength : splitLength+length]
				byteData = append(byteData, availableBytes...)
				if d != length {
					data = data[splitLength+length:]
				}
				length = 0
				var data T
				data, err := chanel.sr.Deserialize(byteData, data)
				rdh(data, chanel, err)
				byteData = nil
			}
		} else {
			if n < length {
				length -= n
				byteData = append(byteData, data...)
			} else {
				i := data[0:length]
				byteData = append(byteData, i...)
				if n != length {
					data = data[length:]
				}
				length = 0
				var data T
				data, err := chanel.sr.Deserialize(byteData, data)
				rdh(data, chanel, err)
				byteData = nil
			}
		}
	}
}
